package games.platformer;

import games.platformer.entities.*;

import java.awt.Rectangle;
import java.util.Collection;

import vooga.behaviors.MoveBehavior;
import vooga.core.AbstractLevel;
import vooga.core.Player;
import vooga.core.VoogaView;
import vooga.entities.Entity;
import vooga.geom.Vector;
import java.util.Iterator;

/**
 * The logic, for the game
 * 
 * created 12-10
 * @author Ben Shelton
 */
public class Level extends AbstractLevel {
	
	// CONSTANTS
	
	// size of virtual window
	
	final static int WIDTH = 640;
	final static int HEIGHT = 480;
	
	final static double GRAVITY_AMOUNT = 0.75;
	
	final static int BLOCK_PERSIST_TIME = 60;
	
	final static int POINTS_KEY = 500;
	
	final static Rectangle LEVEL_BOUNDS = new Rectangle(0, 0, WIDTH, HEIGHT);
	
	// keyboard move, jump, and climb speeds
	final static int kbdMoveSpeed = 5;
	final static int kbdJumpSpeed = 12;
	final static int kbdClimbSpeed = 5;
	
	// sound file locations
	final static String SOUND_FALL_DOWN = "games/platformer/sounds/dead5.wav";
	final static String SOUND_PICK_UP = "games/platformer/sounds/click1.wav";
	final static String SOUND_JUMP = "games/platformer/sounds/bottlerocket.wav";
	final static String SOUND_NEXT_LEVEL = "games/platformer/sounds/jump_gate.au";
	final static String SOUND_ENEMY = "games/platformer/sounds/scream.aif";
	
	// NOT CONSTANTS
	
	vooga.actions.KeyboardHandler myKeyboardHandler;
	
	// state variables
	
	// objects used in game

	private PlayerCharacter myPlayerCharacter;
	private Collection<Entity> myBlocks;
	
	private Collection<Entity> myLevelElements;
	private Collection<Entity> myEnemies;
	private Collection<Entity> myKeys;
	private Collection<Entity> myLadders;
	private Collection<Entity> myVanishingBlocks;
	private ExitDoor myExitDoor;
	
	public Level(VoogaView view, Player player) 
	{
		super(view, player, LEVEL_BOUNDS);
		setPhysicalSize(new java.awt.Dimension(LEVEL_BOUNDS.width, LEVEL_BOUNDS.height));

		myKeyboardHandler = new vooga.actions.KeyboardHandler();
		
		addKeyListener(myKeyboardHandler);
	}
	
	public void init() { // initialize everything from the entities supplied by the LevelFactory
		
		myBlocks = new java.util.ArrayList<Entity>();
		if(getEntities("blocks") != null)
			myBlocks.addAll(getEntities("blocks"));
		
		myVanishingBlocks = new java.util.ArrayList<Entity>();
		if(getEntities("vanishingblocks") != null)
			myVanishingBlocks.addAll(getEntities("vanishingblocks"));
		
		myLevelElements = new java.util.ArrayList<Entity>();
		myLevelElements.addAll(myBlocks);
		myLevelElements.addAll(myVanishingBlocks);
		
		myEnemies = new java.util.ArrayList<Entity>();
		if(getEntities("enemies") != null)
			myEnemies.addAll(getEntities("enemies"));
		
		myKeys = new java.util.ArrayList<Entity>();
		if(getEntities("keys") != null)
			myKeys.addAll(getEntities("keys"));
		
		myLadders = new java.util.ArrayList<Entity>();
		if(getEntities("ladders") != null)
			myLadders.addAll(getEntities("ladders"));

		myExitDoor = (ExitDoor) getEntities("exitdoor").iterator().next();
		
		myPlayerCharacter = (PlayerCharacter) getEntities("playercharacter").iterator().next();
		
		Iterator it = myVanishingBlocks.iterator(); // this doesn't work for more than one vanishing block - one overwrites the next - this behavior will have to be rewritten to support an input of a collection of vanishing blocks
		while(it.hasNext()) // this only gets called initially - calling it again resets the timer
			myPlayerCharacter.addBehavior(new games.platformer.behaviors.RemoveAfterTime(BLOCK_PERSIST_TIME, this, (Entity) it.next()));
		
		createBehaviors();
	}
	
	private void createBehaviors() // create all the behaviors, actions, and key assignments - this will need to be called initially and every time something is removed
	{
		myPlayerCharacter.addBehavior(new MoveBehavior());
		myPlayerCharacter.addBehavior(new games.platformer.behaviors.IntersectAndMoveToTop(myPlayerCharacter.getVelocity(),myLevelElements));
		myPlayerCharacter.addBehavior(new games.platformer.behaviors.GravityBehavior(myLadders, GRAVITY_AMOUNT));
		myPlayerCharacter.addBehavior(new games.platformer.behaviors.IntersectAndRemoveSound(myPlayerCharacter.getVelocity(), myKeys, this, SOUND_PICK_UP));
		myPlayerCharacter.addBehavior(new games.platformer.behaviors.FallDownAndDie(HEIGHT, this, SOUND_FALL_DOWN));
		myPlayerCharacter.addBehavior(new games.platformer.behaviors.IntersectAndWinLevel(myPlayerCharacter.getVelocity(), myExitDoor, this, SOUND_NEXT_LEVEL, this.getPlayer()));
		myPlayerCharacter.addBehavior(new games.platformer.behaviors.IntersectAndDie(myPlayerCharacter.getVelocity(), myEnemies, this, SOUND_ENEMY));
		
		// We realize that storing soundfile names with constants isn't the best way to do this, but we didn't have time to do it a more elegant way.
		// If we had more time, we would create a SoundBank() object that can be created from a given definition file
		// and contains the specific sounds for specific behaviors/actions.
		// We could also use a properties list to store these filenames, like what we did in Websta.
		
		Iterator enit = myEnemies.iterator();
		while(enit.hasNext())
		{
			Entity tempEntity = (Entity) enit.next();
			tempEntity.addBehavior(new MoveBehavior());
			tempEntity.addBehavior(new games.platformer.behaviors.EnemyBehavior(20, 10));
		}
		
		myKeyboardHandler.addKeyPressed(java.awt.event.KeyEvent.VK_RIGHT, new games.platformer.actions.PlatformerMoveAction(myPlayerCharacter,Vector.RIGHT,kbdMoveSpeed));
		myKeyboardHandler.addKeyPressed(java.awt.event.KeyEvent.VK_LEFT, new games.platformer.actions.PlatformerMoveAction(myPlayerCharacter,Vector.LEFT,kbdMoveSpeed));
		myKeyboardHandler.addKeyPressed(java.awt.event.KeyEvent.VK_SPACE, new games.platformer.actions.JumpAction(myPlayerCharacter,Vector.UP,kbdJumpSpeed, SOUND_JUMP));
		myKeyboardHandler.addKeyPressed(java.awt.event.KeyEvent.VK_UP, new games.platformer.actions.ClimbLadderAction(myPlayerCharacter, myLadders, kbdClimbSpeed));	
		myKeyboardHandler.addKeyPressed(java.awt.event.KeyEvent.VK_DOWN, new games.platformer.actions.ClimbLadderAction(myPlayerCharacter, myLadders, -1 * kbdClimbSpeed));	
	}
	
	//have to be able to remove different objects differently
	public void removeEntity(Entity e) // when an object is removed, this routine removes it from the proper places
	{
		super.removeEntity(e);
		
		if(e instanceof DoorKey)
		{
			myKeys.remove(e);
			this.getPlayer().getStatus().addScore(POINTS_KEY);
		}
		else if(e instanceof LadderBlock)
			myLadders.remove(e);
		else if(e instanceof PlatformBlock)
		{
			myBlocks.remove(e);
			myLevelElements.remove(e);
		}
		else if(e instanceof VanishingBlock)
		{
			myVanishingBlocks.remove(e);
			myLevelElements.remove(e);
		}
		
		createBehaviors(); // recreate behaviors with changed Collections in case anything changed
		
	}
	
	public Collection<Entity> getDoorKeys()
	{
		return myKeys;
	}
	
	public void loseLife()
	{
		getPlayer().getStatus().addLives(-1);
		System.out.println("Restarting the level...");
		this.restartLevel();
	}
	
	public void checkLevelOver()
	{
		
	}
	
	public void toNextLevel()
	{
		System.out.println("You won!");
		nextLevel();
	}
	/**
	 * checks to see if the first player has no more lives
	 */
	protected void checkGameOver()
	{

	}

}
